---
breakpoint: tablet
title: Themeable & styled components
path: /styling-components/
---

# Themeable & styled components

Bumbag has two methods of styling components, via:

- **[style props](/the-box-primitive/style-props) (recommended)**: Allowing you to add CSS as props on your components.
- **[Themeable components](#creating-a-basic-themeable-component) (recommended)**: Allowing you to create components that have a customized theme/style attached to them. This allows you to **style components**, as well as set **default props**, **variants** and **modes** to them.
- **[Styled components](#theming-external-components)**: Allowing you to create a standalone styled component.

**Note: Themeable components are not supported with third-party components that do not belong to Bumbag. [You can use styled components instead](#theming-external-components).**

## Creating a basic themeable component

To create a themeable component, import the `applyTheme` function and then invoke it with your desired component & theme configuration.

In this example, we are creating a themed button component, where the second parameter is the [Button theme config](/components/button/#theming).

Each component theme configuration always consists of 4 attributes:

- `styles` - Where the main styling is defined for the component.
- `variants` - Where the [component variants](/variants) are defined.
- `modes` - Where the [component color modes](/color-modes) are defined.
- `defaultProps` - The default props to apply to the component.

For each docs page of the component, you will find a [**Theming** section](/components/button/#theming) where you can reference the schema of the component theme configuration.

##### Here is a basic example:

```function-live noInline
// import { Button, applyTheme, css } from 'bumbag';

const FancyButton = applyTheme(Button, {
  styles: {
    base: {
      fontSize: '18px',
      padding: '1rem 2rem'
    }
  },
  defaultProps: {
    palette: 'primary',
    variant: 'outlined'
  }
});

function Example() {
  return (
    <FancyButton>Hello world</FancyButton>
  );
}

render(Example)
```

##### Here is a more complex example using all the attributes:

[Read more on theme-based values](#generating-styles-based-from-the-theme)

```function-live noInline
const FancyButton = applyTheme(Button, {
  styles: {
    base: {
      // Here we are using a "border" value from the theme.
      // But you can specify a CSS value too.
      border: 'default',

      // You can specify a "palette" from the theme.
      color: 'primary',

      // Even pseudo selectors
      ':hover': {
        color: 'secondary'
      },

      // Or breakpoint values
      // (try resize your window to a tablet viewport)
      fontSize: { 'max-tablet': '14px' }
    }
  },
  variants: {
    'call-to-action': {
      styles: {
        base: {
          // Here we are using "spacing" values from the theme.
          // But you can specify a `px`, `rem` or `em` value too.
          paddingY: 'major-2',
          paddingX: 'major-4'
        }
      }
    }
  },
  modes: {
    dark: {
      styles: {
        base: {
          borderColor: 'gray700'
        }
      }
    }
  },
  defaultProps: {
    variant: 'ghost'
  }
});

function Example() {
  return (
    <FancyButton variant="call-to-action">Hello world</FancyButton>
  );
}

render(Example)
```

## Theming external components

Unfortunately, it is not possible to use `applyTheme` to components that do not belong to Bumbag. However, Bumbag exports [Emotion's `styled` function](https://emotion.sh/docs/styled) to help you style external components.

**If you want, you could also use this with Bumbag components.**

```jsx
import { styled, css } from 'bumbag';
import CustomComponent from 'external-library';

const FancyComponent = styled(CustomComponent)`
  border: 1px solid black;
  padding: 1rem 2rem;
`;

function Example() {
  return (
    <FancyComponent>Hello world</FancyComponent>
  );
}

render(Example)
```

## Generating styles based from props

#### With `applyTheme`

You can generate styles based on your props (and changes in props) by supplying a function (`props => ...`) to your theme attribute:

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: props => ({
      backgroundColor: props.palette,
      ...(props.type === 'rounded' && ({ borderRadius: '4' }))
    })
  }
});

function Example() {
  return (
    <FancyBox
      palette="red"
      color="white"
      type="rounded"
    >
      Hello world
    </FancyBox>
  );
}

render(Example)
```

#### With `styled`

```jsx
import { Box, styled, css } from 'bumbag';

const FancyBox = styled(Box)`
  background-color: ${props => props.palette};

  ${props => props.type === 'rounded' && css`
    border-radius: 10px;
  `}
`;

function Example() {
  return (
    <FancyBox palette="pink" type="rounded">Hello world</FancyBox>
  );
}

render(Example)
```

## Generating styles based from the theme

### Palette

#### With `applyTheme`

You can specify a value from the [palette](/palette) in the global theme on any color CSS attribute.

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: {
      backgroundColor: 'primary',
      color: 'primaryTint'
    }
  }
});

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

#### With `styled`

Bumbag exports a `palette` helper function to retrieve a color from the palette.

```jsx
import { Box, styled, palette } from 'bumbag';

const FancyBox = styled(Box)`
  background-color: ${palette('primary')};
  color: ${palette('primaryTint')};
`;

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

### Spacing

#### With `applyTheme`

You can specify a value from the [spacing](/spacing) in the global theme on any space-based CSS attribute (such as margins & paddings).

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: {
      paddingY: 'major-2',
      paddingX: 'major-4',
      backgroundColor: 'primary',
      color: 'primaryTint'
    }
  }
});

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

#### With `styled`

Bumbag exports a `space` helper function to retrieve a space from the spacing theme.

```jsx
import { Box, styled, palette } from 'bumbag';

const FancyBox = styled(Box)`
  padding: ${space(2, 'major')}rem ${space(4, 'major')}rem;
  background-color: ${palette('primary')};
  color: ${palette('primaryTint')};
`;

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

### Fonts

#### With `applyTheme`

You can specify a value from the [fonts](/fonts) in the global theme on any font CSS attribute.

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: {
      fontFamily: 'Comic Sans MS',
      fontSize: '400',
      fontWeight: 'semibold'
    }
  }
});

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

#### With `styled`

Bumbag exports a `font`, `fontSize` and `fontWeight` helper function to retrieve a font from the theme.

```jsx
import { Box, styled, font, fontSize, fontWeight } from 'bumbag';

const FancyBox = styled(Box)`
  font-family: ${font('Comic Sans MS')};
  font-size: ${fontSize('400')};
  font-weight: ${fontWeight('semibold')};
`;

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

### Borders

#### With `applyTheme`

You can specify a value from the [borders](/the-box-primitive/borders) in the global theme on any border CSS attribute.

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: {
      border: 'default'
    }
  }
});

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

#### With `styled`

Bumbag exports a `border` helper function to retrieve a border from the theme.

```jsx
import { Box, styled, border, palette } from 'bumbag';

const FancyBox = styled(Box)`
  border: ${border('default')};
`;

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

### Border radius

#### With `applyTheme`

You can specify a value from the [border radius](/the-box-primitive/border-radius) in the global theme on the `borderRadius` CSS attribute.

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: {
      border: 'default',
      borderRadius: '4'
    }
  }
});

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

#### With `styled`

Bumbag exports a `borderRadius` helper function to retrieve a border from the theme.

```jsx
import { Box, styled, borderRadius, palette } from 'bumbag';

const FancyBox = styled(Box)`
  border-radius: ${borderRadius('4')};
`;

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

### Breakpoints

#### With `applyTheme`

You can specify breakpoint based values which will apply when the viewport hits a specific [breakpoint](/breakpoints).

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: {
      color: { default: 'primary', 'max-desktop': 'secondary' }
    }
  }
});

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

#### With `styled`

Bumbag exports a `breakpoint` helper function to assist with breakpoint media queries.

```jsx
import { Box, styled, css, breakpoint, palette } from 'bumbag';

const FancyBox = styled(Box)`
  color: ${palette('primary')};

  ${breakpoint('max-desktop', css`
    color: ${palette('secondary')};
  `)}
`;

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

### Pseudo selectors

#### With `applyTheme`

You can also specify pseudo selectors.

```function-live noInline
const FancyBox = applyTheme(Box, {
  styles: {
    base: {
      ':hover': {
        color: 'primary'
      }
    }
  }
});

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```

#### With `styled`

```jsx
import { Box, styled, palette } from 'bumbag';

const FancyBox = styled(Box)`
  &:hover {
    color: ${palette('primary')};
  }
`;

function Example() {
  return (
    <FancyBox>Hello world</FancyBox>
  );
}

render(Example)
```
